// standard library
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <set>
#include <vector>
#include <utility>

using namespace std;

int main(int argc, char* argv[])
{

  //
  int nb_nodes(0), n1, n2, k_max(0), k1, k2, c_max(0), c1, c2;


  // ==============================================================================================
  // loads the edge list
  // ==============================================================================================

  // stream accessing the file
  string EdgeList_filename = argv[1];
  EdgeList_filename += "EdgeList.dat";
  ifstream EdgeList_file(EdgeList_filename.c_str());

  // the edge list itself
  set< pair<int,int> > edgelist;

  // fills the edge list from file
  if(EdgeList_file.is_open())
  {
    while(!EdgeList_file.eof())
    {
      // reads an edge
      EdgeList_file >> n1 >> n2 >> ws;
      // insert the edge with the small tag first
      if(n1 < n2)
        edgelist.insert(make_pair(n1,n2));
      if(n1 > n2)
        edgelist.insert(make_pair(n2,n1));
      // computes the number of nodes
      if(n1 > nb_nodes)
        nb_nodes = n1;
      if(n2 > nb_nodes)
        nb_nodes = n2;
    }
    ++nb_nodes; // nodes' tag begins at zero, therefore the number of nodes is the highest tag plus one
    EdgeList_file.close();
  }
  else
  {
    std::cout << "Failed to open file! (" << EdgeList_filename << ")" << std::endl;
    abort();
  }

  // Note: This algorithm does not consider nodes whose degree is zero and whose tag is greater
  //       than the tag of the last node with a non-zero degree. Too bad for them...


  // ==============================================================================================
  // loads the list of nodes' coreness
  // ==============================================================================================

  // stream accessing the file
  string Coreness_filename = argv[1];
  Coreness_filename += "Coreness.dat";
  ifstream Coreness_file(Coreness_filename.c_str());

  // the coreness list itself
  vector<int> coreness(nb_nodes);

  // fills the coreness list from file
  if(Coreness_file.is_open())
  {
    for(int n(0); n<nb_nodes; ++n)
    {
      // reads an edge
      Coreness_file >> c1 >> ws;
      coreness[n] = c1;
      // finds the highest coreness
      if(c1 > c_max)
        c_max = c1;
    }
    Coreness_file.close();
  }
  else
  {
    std::cout << "Failed to open file! (" << Coreness_filename << ")" << std::endl;
    abort();
  }


  // ==============================================================================================
  // loads the degree sequence
  // ==============================================================================================

  // stream accessing the file
  string Degree_filename = argv[1];
  Degree_filename += "Degrees.dat";
  ifstream Degree_file(Degree_filename.c_str());

  // the degree sequence itself
  vector<int> degree(nb_nodes);

  // fills the degree sequence from file
  if(Degree_file.is_open())
  {
    for(int n(0); n<nb_nodes; ++n)
    {
      // reads an edge
      Degree_file >> k1 >> ws;
      degree[n] = k1;
      // finds the highest coreness
      if(k1 > k_max)
        k_max = k1;
    }
    Degree_file.close();
  }
  else
  {
    std::cout << "Failed to open file! (" << Degree_filename << ")" << std::endl;
    abort();
  }


  // ==============================================================================================
  // CxK matrix (coreness x degree)
  // ==============================================================================================

  // the matrix itself
  vector< vector<int> > CxK(c_max+1, vector<int>(k_max+1,0));

  // fills the matrix
  for(int n(0); n<nb_nodes; ++n)
  {
    c1 = coreness[n];
    k1 = degree[n];
    ++CxK[c1][k1];
  }

  // writes the matrix into file
  string CxK_filename = argv[1];
  CxK_filename += "_CxK.dat";
  ofstream CxK_file(CxK_filename.c_str());

  for(c1=0; c1<=c_max; ++c1)
  {
    for(k1=0; k1<=k_max; ++k1)
    {
      // cout<</*c1<<"  "<<k1<<*/"  "<<CxK[c1][k1]<<"  ";
      CxK_file << CxK[c1][k1] << "  ";
    }
    // cout<<endl;
    CxK_file << endl;
  }

  CxK_file.close();


  // ==============================================================================================
  // CxC matrix (coreness x coreness) and KxK matrix (degree x degree)
  // ==============================================================================================

  // the matrices themselves
  vector< vector<int> > CxC(c_max+1, vector<int>(c_max+1,0));
  vector< vector<int> > KxK(k_max+1, vector<int>(k_max+1,0));

  // fills the matrices
  set< pair<int,int> >::iterator it  = edgelist.begin();
  set< pair<int,int> >::iterator end = edgelist.end();
  for(; it!=end; ++it)
  {
    // nodes involved
    n1 = it->first;
    n2 = it->second;

    // CxC matrix
    c1 = coreness[n1];
    c2 = coreness[n2];
    if(c1 < c2)
      ++CxC[c1][c2];
    else if(c2 < c1)
      ++CxC[c2][c1];
    else
      ++CxC[c1][c2];

    // KxK matrix
    k1 = degree[n1];
    k2 = degree[n2];
    if(k1 < k2)
      ++KxK[k1][k2];
    else if(k2 < k1)
      ++KxK[k2][k1];
    else
      ++KxK[k1][k2];
  }

  // writes the CxC matrix into file
  string CxC_filename = argv[1];
  CxC_filename += "_CxC.dat";
  ofstream CxC_file(CxC_filename.c_str());

  for(c1=0; c1<=c_max; ++c1)
  {
    for(c2=0; c2<=c_max; ++c2)
    {
      CxC_file << CxC[c1][c2] << "  ";
    }
    CxC_file << endl;
  }

  CxC_file.close();

  // writes the KxK matrix into file
  string KxK_filename = argv[1];
  KxK_filename += "_KxK.dat";
  ofstream KxK_file(KxK_filename.c_str());

  for(k1=0; k1<=k_max; ++k1)
  {
    for(k2=0; k2<=k_max; ++k2)
    {
      KxK_file << KxK[k1][k2] << "  ";
    }
    KxK_file << endl;
  }

  KxK_file.close();


}
